package com.icehelper.hades.base.controller;


import java.util.StringJoiner;
import javax.servlet.http.HttpServletRequest;


import com.icehelper.hades.base.exception.CommonErrorCode;
import com.icehelper.hades.base.exception.HadesErrorCodeException;
import com.icehelper.hades.base.exception.HadesFastFailException;
import com.icehelper.hades.base.log.HadesLogger;
import com.icehelper.hades.base.util.Inspections;
import javax.validation.ConstraintViolationException;

import com.icehelper.hades.base.util.JsonHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/** controller aop */
@RestControllerAdvice
public class BaseControllerAdvice implements ResponseBodyAdvice{

  private static HadesLogger logger = HadesLogger.create(BaseControllerAdvice.class);

  @ExceptionHandler( HadesFastFailException.class)
  public BizResponse handleAutoFastFailException( HadesFastFailException ex,
      HttpServletRequest request) {
    logger.dlDebug("FastFail", "fast failed.", ex);
    BizResponse res = new BizResponse();
    res.setCode(ex.getErrorCode().code());
    res.setMessage(
        Inspections.isBlank(ex.getMessage()) ? ex.getErrorCode().msg() : ex.getMessage());
    return res;
  }

  @ExceptionHandler( HadesErrorCodeException.class)
  public BizResponse handleAutoErrorCodeException( HadesErrorCodeException ex,
      HttpServletRequest request) {
    logger.dlDebug("business", "business error.", ex);
    BizResponse res = new BizResponse();
    res.setCode(ex.getErrorCode().code());
    res.setMessage(
        Inspections.isBlank(ex.getMessage()) ? ex.getErrorCode().msg() : ex.getMessage());
    return res;
  }



  /**
   * 处理 JSR-303 Validation 验证Bean失败的错误和数据类型的错误
   */
  @ExceptionHandler(BindException.class)
  @ResponseBody
  public BizResponse handleSpringBindException(BindException ex, HttpServletRequest request) {
    logger.error("param bean validation error. {}", ex.getLocalizedMessage());
    BizResponse res = new BizResponse();
    res.setCode( CommonErrorCode.PARAM_ERROR.code());
    StringJoiner sj = new StringJoiner(",");
    for (ObjectError err : ex.getAllErrors()) {
      if (err instanceof FieldError && TypeMismatchException.ERROR_CODE.equals(err.getCode())) {
        sj.add(((FieldError) err).getField() + "数据类型不正确");
      } else {
        sj.add(err.getDefaultMessage());
      }
    }
    String errMsg = sj.toString();
    res.setMessage(StringUtils.isNotBlank(errMsg) ? errMsg : CommonErrorCode.PARAM_ERROR.msg());
    return res;
  }

  /**
   * 处理 JSR-303 Validation 验证Controller参数失败的错误和数据类型的错误
   */
  @ExceptionHandler( ConstraintViolationException.class)
  @ResponseBody
  public BizResponse handleConstraintViolationException(ConstraintViolationException ex,
      HttpServletRequest request) {

    logger.error("param constraint violation. {}", ex.getLocalizedMessage());
    BizResponse res = new BizResponse();
    res.setCode(CommonErrorCode.PARAM_ERROR.code());
    StringJoiner sj = new StringJoiner(",");
    ex.getConstraintViolations().forEach(cv -> sj.add(cv.getMessage()));
    String errMsg = sj.toString();
    res.setMessage(StringUtils.isNotBlank(errMsg) ? errMsg : CommonErrorCode.PARAM_ERROR.msg());
    return res;
  }

  @ExceptionHandler(Exception.class)
  @ResponseBody
  public BizResponse handleException(Exception ex, HttpServletRequest request) {

    logger.error("error, {}", ex);
    return BizResponse.of(CommonErrorCode.SERVER_BUSY, ex.getLocalizedMessage());
  }


  @Override
  public boolean supports(MethodParameter returnType, Class converterType) {
    return false;
  }

  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType,
      MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request,
      ServerHttpResponse response) {
    String mediaType = selectedContentType.toString();
    if (StringUtils.containsIgnoreCase(mediaType, "text")
        || StringUtils.containsIgnoreCase(mediaType, "json")
        || StringUtils.containsIgnoreCase(mediaType, "xml")) {
      String uri = request.getURI().getPath();
      logger.addKv("uri", uri).logger().info("sending response:{}",
          StringUtils.abbreviate( JsonHelper.toJson(body), 2048));
    }
    return body;

  }
}
